package ch03;

import javax.print.DocFlavor;
import java.util.Queue;

public class BST<Key extends Comparable<Key>, Value> {
    private class Node {
        private Key key;
        private Value value;
        private Node left, right;
        private int N;

        public Node(Key key, Value value, int n) {
            this.key = key;
            this.value = value;
            this.N = n;
        }
    }

    private Node root;

    public int size() {
        return size(root);
    }

    private int size(Node x) {
        if (x == null) return 0;
        return x.N;
    }

    public Value get(Key key) {
        return get(root, key);
    }

    private Value get(Node x, Key key) {
        if (x == null)
            return null;
        int cmp = key.compareTo(x.key);
        if (cmp < 0) {
            return get(x.left, key);
        } else if (cmp > 0) {
            return get(x.right, key);
        } else
            return x.value;
    }

    public void put(Key key, Value value) {
        root = put(root, key, value);
    }

    private Node put(Node x, Key key, Value value) {
        if (x == null) return new Node(key, value, 1);
        int cmp = key.compareTo(x.key);
        if (cmp < 0) {
            x.left = put(x.left, key, value);
        } else if (cmp > 0) {
            x.right = put(x.right, key, value);
        } else {
            x.value = value;
        }
        x.N = size(x.left) + size(x.right) + 1;

        return x;

    }

    public Node min() {
        return min(root);
    }

    private Node min(Node x) {
        if (x.left == null) return x;
        return min(x.left);
    }

    public Node max() {
        return max(root);
    }


    private Node max(Node x) {
        if (x.right == null) {
            return x;
        }
        return max(x.right);
    }

    public Node floor(Key key) {
        return floor(root, key);

    }

    //小于等于key的最大值
    /*
     * 如果key<=x.key则目标键一定在x的左子树中，
     * 如果key>x.key,那么只有根节点右子树存在小于等于key的时候，目标节点才会在右子树中，
     * 否则目标节点为x
     * */
    private Node floor(Node x, Key key) {

        int cmp = key.compareTo(x.key);

        if (cmp < 0) {
            return floor(x.left, key);
        }
        if (cmp == 0) {
            return x;
        }

        Node t = floor(x.right, key);
        if (t != null) {
            return t;
        }
        return x;


    }

    public Key select(int k) {
        return select(root, k);
    }

    //查找排名为k的键
    /*
     * 如果t>k则递归的在左子树搜索，
     * 如果给定的键和根节点(t=k)相同，就返回该节点，
     * 如果t<k则递归的在右子树搜索排名为k-t-1的键
     *
     * */
    public Key select(Node x, int k) {
        if (x == null) {
            return null;
        }
        int t = x.N;
        if (t > k) {
            return select(x.left, k);
        } else if (t == k) {
            return x.key;
        } else {
            return select(x.right, k - t - 1);

        }
    }

    public int rank(Key key) {
        return rank(root, key);
    }

    private int rank(Node x, Key key) {
        if (x == null) {
            return 0;
        }

        int cmp = key.compareTo(x.key);
        if (cmp < 0) {
            return rank(x.left, key);
        } else if (cmp == 0) {
            return size(x);
        } else {
            return 1 + size(x.left) + rank(x.right, key);
        }

    }

    public Node deleteMin() {
        return deleteMin(root);
    }

    private Node deleteMin(Node x) {

        if (x.left == null) {
            return x.right;
        }
        x.left = deleteMin(x.left);
        x.N = size(x.left) + size(x.right) + 1;

        return x;

    }

    public void delete(Key key) {
        root = delete(key, root);
    }

    private Node delete(Key key, Node x) {
        if (x == null) return null;

        int cmp = key.compareTo(x.key);
        if (cmp < 0) x.left = delete(key, x.left);
        else if (cmp > 0) x.right = delete(key, x.right);
        else {
            if (x.right == null) return x.left;
            if (x.left == null) return x.right;
            Node t = x;
            x = min(t.right);
            x.right = deleteMin(t.right);
            x.left = t.left;
        }
        x.N = size(x.left) + size(x.right) + 1;
        return x;
    }


    public void keys(Node x, Queue<Key> queue, Key lo, Key hi) {
        if (x == null) {
            return;
        }
        int cmplo = lo.compareTo(x.key);
        int cmphi = hi.compareTo(x.key);

        if (cmplo < 0)
            keys(x.left, queue, lo, hi);
        if (cmplo <= 0 && cmphi >= 0) {
            queue.add(x.key);
        }
        if (cmphi > 0) {
            keys(x.right, queue, lo, hi);
        }
    }


}
